#pragma once
#include <memory>
#include <functional>
#include <ucontext.h>
#include <atomic>


//#include "demo08.h"

//使用【enable_shared_from_this】需要注意的是：
//1.不可以在MyClass的构造函数中使用shared_from_this()，因为父类的弱针指成员还没有被初始化。
//2.对象直接在栈上或堆上构建，也是不能使用shared_from_this()的，因为常规的构造，不会初始化-父类的弱指针，原理同上。
//3.如果要安全使用shared_from_this(），对象生存至始至终必须由shared_ptr管理，第一个shared_ptr将负责初始化那个父类的弱指针。花了一下午的时间才搞清楚，
//4.使用shared_from_this(）得到的智能指针，显然不是这个对象的第一个智能指针。
class Scheduler;

class Fiber : public std::enable_shared_from_this<Fiber> {
friend class Scheduler;
public:
    typedef std::shared_ptr<Fiber> ptr;

    enum State {
	    INIT,   // 初始状态：协程刚被创建时的状态
	    HOLD,   // 挂起状态(暂停状态)：协程A未执行完被暂停去执行其他协程，需要保存上下文信息
	    EXEC,   // 运行状态(执行中状态)：协程正在执行的状态
	    TERM,   // 结束状态：协程运行结束
	    READY,  // 就绪状态(可执行状态)：协程可以执行
	    EXCEPT, // 异常结束状态：协程发生异常结束
    };
private:
	//将默认构造函数私有，不允许调用默认构造，由此推断出必然有一个自定义的公共有参构造
    Fiber();

public:
	//自定义公共有参构造,提供回调方法和最大方法调用栈信息层数
    Fiber(std::function<void()> cb, size_t stacksize = 0, bool use_caller = false);
    ~Fiber();

    //重置协程函数，并重置状态，重置后该内存可以用在新协程的使用上，节约空间。
    //INIT，TERM
    void reset(std::function<void()> cb);
    //切换到子协程执行
    void swapIn();
    //切换到主协程执行
    void swapOut();
    //调用指定协程执行
    void call();
    //当前线程切换到后台 执行的为该协程 返回到线程的主协程
    void back();
    //获取协程id
    uint64_t getId() const {return m_id;}
    //获取协程状态
    State getState() const { return m_state;}
public:
    //设置当前协程
    static void SetThis(Fiber* f); //如果没有协程会初始化一个主协程
    //返回当前协程
    static Fiber::ptr GetThis();
    //协程切换到后台，并且设置为Ready状态
    static void YieldToReady();
    //协程切换到后台，并且设置为Hold状态
    static void YieldToHold();
    //总协程数
    static uint64_t TotalFibers();
    //获取协程ID
    static uint64_t GetFiberId();
    //协程执行函数 执行完成返回到线程主协程
    static void MainFunc();
    //协程执行函数 执行完成返回到线程调度协程
    static void CallerMainFunc();

private:
	//协程ID
    uint64_t m_id = 0;
    //协程栈大小
    uint32_t m_stacksize = 0;
    //协程状态
    State m_state = INIT;
	//上下文
    ucontext_t m_ctx;
    //当前栈指针
    void* m_stack = nullptr;
	//回调函数(协程中运行的方法)
    std::function<void()> m_cb;
    
};
